﻿namespace CookieTerm
{
    using System;
    using System.Configuration;
    using System.Globalization;
    using System.IO;
    using System.Reflection;

    internal static class CookieTermAppSettings
    {
        static CookieTermAppSettings()
        {
            AppConfig.LoadAppSettings(typeof(CookieTermAppSettings));
        }

        [AppSetting]
        public static string ApplicationName { get; set; }

        [AppSetting]
        public static string BookmarkFile { get; set; }

        [AppSetting]
        public static int TelnetBufferSize { get; set; }

        [AppSetting]
        public static bool InterpreterThrows { get; set; }

        [AppSetting]
        public static bool DisplayLogo { get; set; }

        [AppSetting]
        public static uint AntiIdleInterval { get; set; }

        [AppSetting]
        public static int AntiIdleChar { get; set; }
    }

    internal static class AppConfig
    {
        public static void LoadAppSettings(Type classType)
        {
            PropertyInfo[] properties = classType.GetProperties(BindingFlags.Static | BindingFlags.Public);
            foreach (PropertyInfo pi in properties)
            {
                object[] attributes = pi.GetCustomAttributes(typeof(AppSettingAttribute), true);

                if (attributes.Length == 0)
                {
                    continue;
                }

                if (attributes.Length > 1)
                {
                    throw new InvalidOperationException(string.Format("{0}.{1} is marked with multiple AppSettingAttribute.", classType.Name, pi.Name));
                }

                string keyName = ((AppSettingAttribute)attributes[0]).KeyName ?? pi.Name;
                Type valueType = pi.PropertyType;

                pi.SetValue(null, ReadAppSetting(valueType, keyName), null);
            }
        }

        private static object ReadAppSetting(Type type, string name)
        {
            string value = ConfigurationManager.AppSettings.Get(name);

            if (value == null)
            {
                throw new InvalidDataException(string.Format("Cannot read \"{0}\" from configuration", name));
            }

            if (type.Equals(typeof(string)))
            {
                return value;
            }
            else if (type.Equals(typeof(char)))
            {
                return (object)char.Parse(value);
            }
            else if (type.Equals(typeof(int)))
            {
                return (object)int.Parse(value, CultureInfo.InvariantCulture);
            }
            else if (type.Equals(typeof(uint)))
            {
                return (object)uint.Parse(value, CultureInfo.InvariantCulture);
            }
            else if (type.Equals(typeof(double)))
            {
                return (object)double.Parse(value, CultureInfo.InvariantCulture);
            }
            else if (type.Equals(typeof(DateTime)))
            {
                return (object)DateTime.Parse(value, CultureInfo.InvariantCulture);
            }
            else if (type.Equals(typeof(bool)))
            {
                return (object)bool.Parse(value);
            }
            else
            {
                throw new NotImplementedException(string.Format("ReadAppSetting does not support type {0}.", type.Name));
            }
        }
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    internal sealed class AppSettingAttribute : Attribute
    {
        public string KeyName { get; set; }
    }
}
